package vip.wangwenhao.autoconfigure.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import vip.wangwenhao.autoconfigure.service.CacheKeyGenerator;
import vip.wangwenhao.common.annotations.GroupResubmitLock;
import vip.wangwenhao.common.enums.ResultCode;
import vip.wangwenhao.common.exception.CommonException;
import vip.wangwenhao.common.util.RedisLockHelper;

import java.lang.reflect.Method;
import java.util.UUID;

/**
 * 集群防重增强类 基于redis分布式锁
 *
 * @author wangwenhao
 */
@Aspect
@Configuration
public class GroupResubmitLockAspect {

    private final RedisLockHelper redisLockHelper;
    private final CacheKeyGenerator cacheKeyGenerator;

    @Autowired
    public GroupResubmitLockAspect(RedisLockHelper redisLockHelper, CacheKeyGenerator cacheKeyGenerator) {
        this.redisLockHelper = redisLockHelper;
        this.cacheKeyGenerator = cacheKeyGenerator;
    }

    @Around("execution(public * *(..)) && @within(vip.wangwenhao.common.annotations.GroupResubmitLock)")
    public Object interceptor(ProceedingJoinPoint pjp) throws Throwable {
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        GroupResubmitLock groupResubmitLock = method.getAnnotation(GroupResubmitLock.class);
        String key = cacheKeyGenerator.getLockKey(pjp);
        final String lockKey = groupResubmitLock.prefix() + key;
        String value = UUID.randomUUID().toString();
        try {
            // 假设上锁成功，但是设置过期时间失效，以后拿到的都是 false
            final boolean success = redisLockHelper.lock(lockKey, value, groupResubmitLock.expire(), groupResubmitLock.timeUnit());
            if (!success) {
                throw new CommonException(ResultCode.RESUBMIT);
            }
            return pjp.proceed();
        } finally {
            //如果演示的话需要注释该代码;实际应该放开
            redisLockHelper.unlock(lockKey, value);
        }
    }
}